/*
* Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
*
* (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
*                           Jerremy Koot (jkoot@snes9x.com)
*
* Super FX C emulator code 
* (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
*                           Gary Henderson.
* Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
*
* DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
* C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
* C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
* SPC7110 code (c) Copyright 2002 Matthew Kendora, Dark Force, zsKnight,
*						and John Weidman, with fixes and debugging code
*						from CaitSith2
*
* DOS port code contains the works of other authors. See headers in
* individual files.
*
* Snes9x homepage: http://www.snes9x.com
*
* Permission to use, copy, modify and distribute Snes9x in both binary and
* source form, for non-commercial purposes, is hereby granted without fee,
* providing that this license information and copyright notice appear with
* all copies and any derived work.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event shall the authors be held liable for any damages
* arising from the use of this software.
*
* Snes9x is freeware for PERSONAL USE only. Commercial users should
* seek permission of the copyright holders first. Commercial use includes
* charging money for Snes9x or software derived from Snes9x.
*
* The copyright holders request that bug fixes and improvements to the code
* should be forwarded to them so everyone can benefit from the modifications
* in future versions.
*
* Super NES and Super Nintendo Entertainment System are trademarks of
* Nintendo Co., Limited and its subsidiary companies.
*/

//#define SPC7110_DEBUG

#include "spc7110.h"
#include "memmap.h"
#include <time.h>
#include <sys/stat.h>

//Windows includes
#ifdef __WIN32__
#ifndef _XBOX				// chdir and getcwd not supported on Xbox hardware
#include <direct.h>
#define chdir _chdir
#define getcwd _getcwd
#endif
#define FREEZEFOLDER	GUI.FreezeFileDir
//zinx suggested this, for *nix compatibility
#define PATH_MAX	MAX_PATH
#endif

//#ifdef __LINUX__
#ifdef __linux
#include "display.h"
#include <limits.h>
#include <unistd.h>
#define FREEZEFOLDER S9xGetSnapshotDirectory ()
#endif

//really not needed, but usually MS adds the _ to POSIX functions,
//while *nix doesn't, so this was to "un-M$" the function.
#define splitpath _splitpath

//not much headroom, but FEOEZ has 41 tables, I think, and SPL4 has 38.
#define MAX_TABLES 48

//default to using 5 megs of RAM for method 3 caching.
uint16 cacheMegs=5;

//using function pointers to initialize cache management
void (*CleanUp7110)(void)=NULL;
void (*LoadUp7110)(char*)=&SPC7110Load;
void (*Copy7110)(void)=NULL;

//size and offset of the pack data
//offset and size of reads from pack
typedef struct SPC7110DecompressionLocationStruct
{
	uint32 offset;
	uint32 size;
	uint16 used_offset;
	uint16 used_len;
} Data7110;

//this maps an index.bin table to the decompression pack
typedef struct SPC7110DecompressionIndexStruct
{
	int table;
	bool is_file;
	Data7110 location[256];
} Index7110;

//this contains all the data for the decompression pack.
typedef struct SPC7110DecompressionPackStructure
{
	uint8* binfiles[MAX_TABLES];
	Index7110 tableEnts[MAX_TABLES];
	int last_table;
	int idx;
	uint8 last_idx;
	uint16 last_offset;
} Pack7110;


char pfold[9];				//hack variable for log naming (each game makes a different log)
Pack7110* decompack=NULL;   //decompression pack uses a fair chunk of RAM, so dynalloc it.
SPC7110Regs s7r;			//SPC7110 registers, about 33KB
S7RTC rtc_f9;				//FEOEZ (and Shounen Jump no SHou) RTC
void	S9xUpdateRTC ();	//S-RTC function hacked to work with the RTC

//Emulate power on state
void S9xSpc7110Init()
{
	s7r.DataRomOffset=0x00100000;//handy constant!
	s7r.DataRomSize=Memory.CalculatedSize-s7r.DataRomOffset;
	s7r.reg4800=0;
	s7r.reg4801=0;
	s7r.reg4802=0;
	s7r.reg4803=0;
	s7r.reg4804=0;
	s7r.reg4805=0;
	s7r.reg4806=0;
	s7r.reg4807=0;
	s7r.reg4808=0;
	s7r.reg4809=0;
	s7r.reg480A=0;
	s7r.reg480B=0;
	s7r.reg480C=0;
	s7r.reg4811=0;
	s7r.reg4812=0;
	s7r.reg4813=0;
	s7r.reg4814=0;
	s7r.reg4815=0;
	s7r.reg4816=0;
	s7r.reg4817=0;
	s7r.reg4818=0;
	s7r.reg4820=0;
	s7r.reg4821=0;
	s7r.reg4822=0;
	s7r.reg4823=0;
	s7r.reg4824=0;
	s7r.reg4825=0;
	s7r.reg4826=0;
	s7r.reg4827=0;
	s7r.reg4828=0;
	s7r.reg4829=0;
	s7r.reg482A=0;
	s7r.reg482B=0;
	s7r.reg482C=0;
	s7r.reg482D=0;
	s7r.reg482E=0;
	s7r.reg482F=0;
	s7r.reg4830=0;
	s7r.reg4831=0;
	s7r.reg4832=1;
	s7r.reg4833=2;
	s7r.reg4834=0;
	s7r.reg4840=0;
	s7r.reg4841=0;
	s7r.reg4842=0;
	s7r.written=0;
	s7r.offset_add=0;
	s7r.AlignBy=1;

	//establish game specific details

	if(!strncmp((char*)&Memory.ROM [0xffc0], "SUPER POWER LEAG 4   ", 21))
	{
		//super power league
		strcpy(pfold, "SPL4-SP7");
		(*LoadUp7110)("SPL4-SP7");
	}
	else if(!strncmp((char*)&Memory.ROM [0xffc0], "MOMOTETSU HAPPY      ",21))
	{
		//happy
		(*LoadUp7110)("SMHT-SP7");
		strcpy(pfold, "SMHT-SP7");
	}
	else if(!strncmp((char*)&Memory.ROM [0xffc0], "HU TENGAI MAKYO ZERO ", 21))
	{
		//feoez
		(*LoadUp7110)("FEOEZSP7");
		strcpy(pfold, "FEOEZSP7");
		Settings.TurboMode=false;
	}
	else if(!strncmp((char*)&Memory.ROM [0xffc0], "JUMP TENGAIMAKYO ZERO",21))
	{
		//feoez -sjns
		(*LoadUp7110)("SJUMPSP7");
		strcpy(pfold, "SJUMPSP7");
		Settings.TurboMode=false;
	}
	//"support" for part of unknown games.
	else
	{
		(*LoadUp7110)("MISC-SP7");
		strcpy(pfold, "MISC-SP7");
	}
	s7r.bank50Internal=0;
	memset(s7r.bank50,0x00,DECOMP_BUFFER_SIZE);
}


//full cache decompression routine (memcpy) Method 1
void MovePackData()
{
	//log the last entry
	Data7110* log=&(decompack->tableEnts[decompack->idx].location[decompack->last_idx]);
	if((log->used_len+log->used_offset)<(decompack->last_offset+(unsigned short)s7r.bank50Internal))
	{
		log->used_len=s7r.bank50Internal;
		log->used_offset=decompack->last_offset;
	}
	
	//set up for next logging
	decompack->last_offset=(s7r.reg4805)|(s7r.reg4806<<8);
	
	decompack->last_idx=s7r.reg4804;

	//start decompression
	int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;
	
	//the table is a offset multiplier byte and a big-endian pointer
	int j= 4*s7r.reg4804;
	j+=s7r.DataRomOffset;
	j+=table;
	
	//set proper offsetting.
	if(s7r.reg480B==0)
		s7r.AlignBy=0;
	else
	{
		switch(ROM[j])
		{
		case 0x03:
			s7r.AlignBy=8;
			break;
		case 0x01:
			s7r.AlignBy=2;
			break;
		case 0x02:
			s7r.AlignBy=4;
			break;
		case 0x00:
		default:
			s7r.AlignBy=1;
			break;
		}
	}
	//note that we are still setting up for the next log.
	decompack->last_offset*=s7r.AlignBy;
	decompack->last_offset%=DECOMP_BUFFER_SIZE;

	//find the table
	if(table!=decompack->last_table)
	{
		int i=0;
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table)
			i++;
		if(i==MAX_TABLES)
		{
#ifdef _XBOX
			FILE* fp=fopen("T:\\sp7err.out","a");
#else
			FILE* fp=fopen("sp7err.out","a");
#endif

			fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804);
			fclose(fp);
			return;
		}
		decompack->idx=i;
		decompack->last_table=table;
	}

	//copy data
	if(decompack->binfiles[decompack->idx])
	{
		memcpy(s7r.bank50,
			&(decompack->binfiles[decompack->idx][decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset]),
			decompack->tableEnts[decompack->idx].location[s7r.reg4804].size);
	}
}


//this is similar to the last function, but it keeps the last 5 accessed files open,
// and reads the data directly. Method 2
void ReadPackData()
{
	static int table_age_2;
	static int table_age_3;
	static int table_age_4;
	static int table_age_5;
	
	int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;

	if(table==0)
	{
		table_age_2=table_age_3=table_age_4=table_age_5=MAX_TABLES;
		return;
	}

	if(table_age_2==0&&table_age_3==0&&table_age_4==0&&table_age_5==0)
		table_age_2=table_age_3=table_age_4=table_age_5=MAX_TABLES;
	Data7110* log=&(decompack->tableEnts[decompack->idx].location[decompack->last_idx]);
	if((log->used_len+log->used_offset)<(decompack->last_offset+(unsigned short)s7r.bank50Internal))
	{
		log->used_len=s7r.bank50Internal;
		log->used_offset=decompack->last_offset;
	}
	
	decompack->last_offset=(s7r.reg4805)|(s7r.reg4806<<8);
	
	decompack->last_idx=s7r.reg4804;
	
	int j= 4*s7r.reg4804;
	j+=s7r.DataRomOffset;
	j+=table;
	
	if(s7r.reg480B==0)
		s7r.AlignBy=0;
	else
	{
		switch(ROM[j])
		{
		case 0x03:
			s7r.AlignBy=8;
			break;
		case 0x01:
			s7r.AlignBy=2;
			break;
		case 0x02:
			s7r.AlignBy=4;
			break;
		case 0x00:
		default:
			s7r.AlignBy=1;
			break;
		}
	}
	decompack->last_offset*=s7r.AlignBy;
	decompack->last_offset%=DECOMP_BUFFER_SIZE;
	if(table!=decompack->last_table)
	{
		int i=0;
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table)
			i++;
		if(i==MAX_TABLES)
		{
			FILE* fp=fopen("sp7err.out","a");
			fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804);
			fclose(fp);
			return;
		}
		if(i!= table_age_2 && i!= table_age_3 && i!= table_age_4 && i!= table_age_5)
		{
			if(table_age_5!=MAX_TABLES&&decompack->binfiles[table_age_5])
			{
				fclose((FILE*)(decompack->binfiles[table_age_5]));
				(decompack->binfiles[table_age_5])=NULL;
			}
			table_age_5=table_age_4;
			table_age_4=table_age_3;
			table_age_3=table_age_2;
			table_age_2=decompack->idx;
			char name[PATH_MAX];
			//open file
			char drive [_MAX_DRIVE + 1];
			char dir [_MAX_DIR + 1];
			char fname [_MAX_FNAME + 1];
			char ext [_MAX_EXT + 1];
			if (strlen (FREEZEFOLDER))
			{
				//splitpath (Memory.ROMFilename, drive, dir, fname, ext);
				strcpy (name, FREEZEFOLDER);
				strcat (name, "/");
			}
			else
			{
				splitpath (Memory.ROMFilename, drive, dir, fname, ext);
				strcpy(name, drive);
				//strcat(filename, "\\");
				strcat(name, dir);
			}
			strcat(name, pfold);
			char bfname[11];
			sprintf(bfname, "%06X.bin", table);
			strcat(name, "/");
			strcat(name, bfname);
			decompack->binfiles[i]=(uint8*)fopen(name, "rb");
		}
		else
		{
			//fix tables in this case
			if(table_age_5==i)
			{
				table_age_5=table_age_4;
				table_age_4=table_age_3;
				table_age_3=table_age_2;
				table_age_2=decompack->idx;
			}
			if(table_age_4==i)
			{
				table_age_4=table_age_3;
				table_age_3=table_age_2;
				table_age_2=decompack->idx;
			}
			if(table_age_3==i)
			{
				table_age_3=table_age_2;
				table_age_2=decompack->idx;
			}
			if(table_age_2==i)
			{
				table_age_2=decompack->idx;
			}
		}
		decompack->idx=i;
		decompack->last_table=table;
	}
	//do read here.
	if(decompack->binfiles[decompack->idx])
	{
		fseek((FILE*)(decompack->binfiles[decompack->idx]), decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset, 0);
		fread(s7r.bank50,1, (decompack->tableEnts[decompack->idx].location[s7r.reg4804].size), (FILE*)(decompack->binfiles[decompack->idx]));
	}
}

//Cache Method 3: some entries are cached, others are file handles.
//use is_file to distinguish.
void GetPackData()
{
	Data7110* log=&(decompack->tableEnts[decompack->idx].location[decompack->last_idx]);
	if((log->used_len+log->used_offset)<(decompack->last_offset+(unsigned short)s7r.bank50Internal))
	{
		log->used_len=s7r.bank50Internal;
		log->used_offset=decompack->last_offset;
	}
	
	decompack->last_offset=(s7r.reg4805)|(s7r.reg4806<<8);
	
	decompack->last_idx=s7r.reg4804;
	int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;
	
	int j= 4*s7r.reg4804;
	j+=s7r.DataRomOffset;
	j+=table;
	
	if(s7r.reg480B==0)
		s7r.AlignBy=0;
	else
	{
		switch(ROM[j])
		{
		case 0x03:
			s7r.AlignBy=8;
			break;
		case 0x01:
			s7r.AlignBy=2;
			break;
		case 0x02:
			s7r.AlignBy=4;
			break;
		case 0x00:
		default:
			s7r.AlignBy=1;
			break;
		}
	}
	decompack->last_offset*=s7r.AlignBy;
	decompack->last_offset%=DECOMP_BUFFER_SIZE;
	if(table!=decompack->last_table)
	{
		int i=0;
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table)
			i++;
		if(i==MAX_TABLES)
		{
			FILE* fp=fopen("sp7err.out","a");
			fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804);
			fclose(fp);
			return;
		}
		decompack->idx=i;
		decompack->last_table=table;
	}
	if(decompack->binfiles[decompack->idx])
	{
		if(decompack->tableEnts[decompack->idx].is_file)
		{
			fseek((FILE*)decompack->binfiles[decompack->idx], decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset, 0);
			fread(s7r.bank50,1, (decompack->tableEnts[decompack->idx].location[s7r.reg4804].size), (FILE*)(decompack->binfiles[decompack->idx]));
		}
		else
		{
			memcpy(s7r.bank50,
				&(decompack->binfiles[decompack->idx][decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset]),
				decompack->tableEnts[decompack->idx].location[s7r.reg4804].size);
		}
	}
}

extern "C"{
//reads SPC7110 and RTC registers.
uint8 S9xGetSPC7110(uint16 Address)
{
#ifdef SPC7110_DEBUG
	printf("%04X read\n", Address);
#endif
	switch (Address)
	{
		//decompressed data read port. decrements 4809-A (with wrap)
		//4805-6 is the offset into the bank
		//AlignBy is set (afaik) at decompression time, and is the offset multiplier
		//bank50internal is an internal pointer to the actual byte to read.
		//so you read from offset*multiplier + bank50internal
		//the offset registers cannot be incremented due to the offset multiplier.
	case 0x4800:
		{
			unsigned short count=s7r.reg4809|(s7r.reg480A<<8);
			uint32 i, j;
			j=(s7r.reg4805|(s7r.reg4806<<8));
			j*=s7r.AlignBy;
			i=j;
			if(count >0)
				count--;
			else count = 0xFFFF;
			s7r.reg4809=0x00ff&count;
			s7r.reg480A=(0xff00&count)>>8;
			i+=s7r.bank50Internal;
			i%=DECOMP_BUFFER_SIZE;
			s7r.reg4800=s7r.bank50[i];
			
			s7r.bank50Internal++;
			s7r.bank50Internal%=DECOMP_BUFFER_SIZE;
#ifdef SPC7110_DEBUG
			printf("Returned %02X\n", s7r.reg4800);
#endif
		}
		return s7r.reg4800;
	//table register low
	case 0x4801: return s7r.reg4801;
	//table register middle
	case 0x4802: return s7r.reg4802;
	//table register high
	case 0x4803: return s7r.reg4803;
	//index of pointer in table (each entry is 4 bytes)
	case 0x4804: return s7r.reg4804;
	//offset register low
	case 0x4805: return s7r.reg4805;
	//offset register high
	case 0x4806: return s7r.reg4806;
	//DMA channel (not that I see this usually set,
	//regardless of what channel DMA is on)
	case 0x4807: return s7r.reg4807;
	//C r/w option, unknown, defval:00 is what Dark Force says
	//afaict, Snes9x doesn't use this at all.
	case 0x4808: return s7r.reg4808;
	//C-Length low
	//counts down the number of bytes left to read from the decompression buffer.
	//this is set by the ROM, and wraps on bounds.
	case 0x4809: return s7r.reg4809;
	//C Length high
	case 0x480A: return s7r.reg480A;
	//Offset enable.
	//if this is zero, 4805-6 are useless. Emulated by setting AlignBy to 0
	case 0x480B:
		return s7r.reg480B;
	//decompression finished: just emulated by switching each read.
	case 0x480C: 
		s7r.reg480C^=0x80;
		return s7r.reg480C^0x80;
	
	//Data access port
	//reads from the data ROM (anywhere over the first 8 mbits
	//behavior is complex, will document later,
	//possibly missing cases, because of the number of switches in play
	case 0x4810:
		if(s7r.written==0)
			return 0;
		if((s7r.written&0x07)==0x07)
		{
			uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
			i%=s7r.DataRomSize;
			if(s7r.reg4818&0x02)
			{
				if(s7r.reg4818&0x08)
				{
					signed short r4814;
					r4814=(s7r.reg4815<<8)|s7r.reg4814;
					i+=r4814;
					r4814++;
					s7r.reg4815=(uint8)(r4814>>8);
					s7r.reg4814=(uint8)(r4814&0x00FF);
				}
				else
				{
					unsigned short r4814;
					r4814=(s7r.reg4815<<8)|s7r.reg4814;
					i+=r4814;
					if(r4814!=0xFFFF)
						r4814++;
					else r4814=0;
					s7r.reg4815=(uint8)(r4814>>8);
					s7r.reg4814=(uint8)(r4814&0x00FF);
					
				}
			}
			i+=s7r.DataRomOffset;
			uint8 tmp=ROM[i];
			i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
			
			if(s7r.reg4818&0x02)
			{
			}
			else if(s7r.reg4818&0x01)
			{
				if(s7r.reg4818&0x04)
				{
					signed short inc;
					inc=(s7r.reg4817<<8)|s7r.reg4816;
					
					if(!(s7r.reg4818&0x10))
						i+=inc;
					else
					{
						if(s7r.reg4818&0x08)
						{
							signed short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
						}
						else
						{
							unsigned short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
							
						}
					}
					//is signed
				}
				else
				{
					uint16 inc;
					inc=(s7r.reg4817<<8)|s7r.reg4816;
					if(!(s7r.reg4818&0x10))
						i+=inc;
					else
					{
						if(s7r.reg4818&0x08)
						{
							signed short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
						}
						else
						{
							unsigned short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
							
						}
					}
				}
			}
			else
			{
				if(!(s7r.reg4818&0x10))
					i+=1;
				else
				{
					if(s7r.reg4818&0x08)
					{
						signed short r4814;
						r4814=(s7r.reg4815<<8)|s7r.reg4814;
						r4814+=1;
						s7r.reg4815=(r4814&0xFF00)>>8;
						s7r.reg4814=r4814&0xFF;
					}
					else
					{
						unsigned short r4814;
						r4814=(s7r.reg4815<<8)|s7r.reg4814;
						r4814+=1;
						s7r.reg4815=(r4814&0xFF00)>>8;
						s7r.reg4814=r4814&0xFF;
						
					}
				}
			}
			
#ifdef SPC7110_DEBUG
			printf("Returned %02X\n", tmp);
#endif
			
			i%=s7r.DataRomSize;
			s7r.reg4811=i&0x00FF;
			s7r.reg4812=(i&0x00FF00)>>8;
			s7r.reg4813=((i&0xFF0000)>>16);
			return tmp;
		}
		else return 0;
	//direct read address low
	case 0x4811: return s7r.reg4811;
	//direct read address middle
	case 0x4812: return s7r.reg4812;
	//direct read access high
	case 0x4813: return s7r.reg4813;
	//read adjust low
	case 0x4814: return s7r.reg4814;
	//read adjust high
	case 0x4815: return s7r.reg4815;
	//read increment low
	case 0x4816: return s7r.reg4816;
	//read increment high
	case 0x4817: return s7r.reg4817;
	//Data ROM command mode
	//essentially, this controls the insane code of $4810 and $481A
	case 0x4818: return s7r.reg4818;
	//read after adjust port
	//what this does, besides more nasty stuff like 4810,
	//I don't know. Just assume it is a different implementation of $4810,
	//if it helps your sanity
	case 0x481A:
		if(s7r.written==0x1F)
		{
			uint32 i=((s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811);
			if(s7r.reg4818&0x08)
			{
				short adj;
				adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
				i+=adj;
			}
			else
			{
				uint16 adj;
				adj=(s7r.reg4815<<8)|s7r.reg4814;
				i+=adj;
			}
			
			i%=s7r.DataRomSize;
			i+=s7r.DataRomOffset;
			uint8 tmp=ROM[i];
			i=((s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811);
			if(0x60==(s7r.reg4818&0x60))
			{
				i=((s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811);
				
				if(!(s7r.reg4818&0x10))
				{
					if(s7r.reg4818&0x08)
					{
						short adj;
						adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
						i+=adj;
					}
					else
					{
						uint16 adj;
						adj=(s7r.reg4815<<8)|s7r.reg4814;
						i+=adj;
					}
					i%=s7r.DataRomSize;
					s7r.reg4811=i&0x00FF;
					s7r.reg4812=(i&0x00FF00)>>8;
					s7r.reg4813=((i&0xFF0000)>>16);
				}
				else
				{
					if(s7r.reg4818&0x08)
					{
						short adj;
						adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
						adj+=adj;
						s7r.reg4815=(adj&0xFF00)>>8;
						s7r.reg4814=adj&0xFF;
					}
					else
					{
						uint16 adj;
						adj=(s7r.reg4815<<8)|s7r.reg4814;
						adj+=adj;
						s7r.reg4815=(adj&0xFF00)>>8;
						s7r.reg4814=adj&0xFF;
					}
				}
			}
#ifdef SPC7110_DEBUG
			printf("Returned %02X\n", tmp);
#endif
			return tmp;
		}
		else return 0;
	
	//multiplicand low or dividend lowest
	case 0x4820: return s7r.reg4820;
	//multiplicand high or divdend lower
	case 0x4821: return s7r.reg4821;
	//dividend higher
	case 0x4822: return s7r.reg4822;
	//dividend highest
	case 0x4823: return s7r.reg4823;
	//multiplier low
	case 0x4824: return s7r.reg4824;
	//multiplier high
	case 0x4825: return s7r.reg4825;
	//divisor low
	case 0x4826: return s7r.reg4826;
	//divisor high
	case 0x4827: return s7r.reg4827;
	
	//result lowest
	case 0x4828:
		return s7r.reg4828;
	//result lower
	case 0x4829:
		return s7r.reg4829;
	//result higher
	case 0x482A:
		return s7r.reg482A;
	//result highest
	case 0x482B:
		return s7r.reg482B;
	//remainder (division) low
	case 0x482C: return s7r.reg482C;
	//remainder (division) high
	case 0x482D: return s7r.reg482D;
	//signed/unsigned
	case 0x482E: return s7r.reg482E;
	//finished flag, emulated as an on-read toggle.
	case 0x482F:
		if(s7r.reg482F)
		{
			s7r.reg482F=0;
			return 0x80;
		}
		return 0;
		break;

	//SRAM toggle
	case 0x4830:
		return s7r.reg4830;
	//DX bank mapping
	case 0x4831:
		return s7r.reg4831;
	//EX bank mapping
	case 0x4832:
		return s7r.reg4832;
	//FX bank mapping
	case 0x4833:
		return s7r.reg4833;
	//SRAM mapping? We have no clue!
	case 0x4834:
		return s7r.reg4834;
//RTC enable
	case 0x4840:
		if(!Settings.SPC7110RTC)
			return Address>>8;
		return s7r.reg4840;
//command/index/value of RTC (essentially, zero unless we're in read mode
	case 0x4841:
		if(!Settings.SPC7110RTC)
			return Address>>8;
		if(rtc_f9.init)
		{
			S9xUpdateRTC();
			uint8 tmp=rtc_f9.reg[rtc_f9.index];
			rtc_f9.index++;
			rtc_f9.index%=0x10;
#ifdef SPC7110_DEBUG
			printf("$4841 returned %02X\n", tmp);
#endif
			return tmp;
		}
		else return 0;
//RTC done flag
	case 0x4842:
		if(!Settings.SPC7110RTC)
			return Address>>8;
		s7r.reg4842^=0x80;
		return s7r.reg4842^0x80;
	default:
#ifdef SPC7110_DEBUG
		printf("Access to Reg %04X\n", Address);
#endif
		return 0x00;
	}
}
}
void S9xSetSPC7110 (uint8 data, uint16 Address)
{
#ifdef SPC7110_DEBUG
	printf("%04X written to, value %02X\n", Address, data);
#endif
	switch(Address)
	{
//Writes to $4800 are undefined.

	//table low, middle, and high.
	case 0x4801:
		s7r.reg4801=data;
		break;
	case 0x4802:
		s7r.reg4802=data;
		break;
	case 0x4803:
		s7r.reg4803=data;
		break;

	//table index (4 byte entries, bigendian with a multiplier byte)
	case 0x4804:
		s7r.reg4804=data;
		break;

	//offset low
	case 0x4805:
		s7r.reg4805=data;
		break;

	//offset high, starts decompression
	case 0x4806:
		s7r.reg4806=data;
		(*Copy7110)();
		s7r.bank50Internal=0;
		s7r.reg480C&=0x7F;
		break;

	//DMA channel register (Is it used??)
	case 0x4807:
		s7r.reg4807=data;
		break;

	//C r/w? I have no idea. If you get weird values written here before a bug,
	//The Dumper should probably be contacted about running a test.
	case 0x4808:
		s7r.reg4808=data;
		break;

	//C-Length low
	case 0x4809:
		s7r.reg4809=data;
		break;
	//C-Length high
	case 0x480A:
		s7r.reg480A=data;
		break;

	//Offset enable
	case 0x480B:
		{
			s7r.reg480B=data;
			int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;
			
			int j= 4*s7r.reg4804;
			j+=s7r.DataRomOffset;
			j+=table;
			
			if(s7r.reg480B==0)
				s7r.AlignBy=0;
			else
			{
				switch(ROM[j])
				{
				case 0x03:
					s7r.AlignBy=8;
					break;
				case 0x01:
					s7r.AlignBy=2;
					break;
				case 0x02:
					s7r.AlignBy=4;
					break;
				case 0x00:
				default:
					s7r.AlignBy=1;
					break;
				}
			}
//			s7r.decomp_set=true;
		}
		break;
//$4810 is probably read only.

	//Data port address low
	case 0x4811:
		s7r.reg4811=data;
		s7r.written|=0x01;
		break;

	//data port address middle
	case 0x4812:
		s7r.reg4812=data;
		s7r.written|=0x02;
		break;

	//data port address high
	case 0x4813:
		s7r.reg4813=data;
		s7r.written|=0x04;
		break;

	//data port adjust low (has a funky immediate increment mode)
	case 0x4814:
		s7r.reg4814=data;
		if(s7r.reg4818&0x02)
		{
			if((s7r.reg4818&0x20)&&!(s7r.reg4818&0x40))
			{
				s7r.offset_add|=0x01;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						if(s7r.reg4818&0x08)
						{
							i+=(signed char)s7r.reg4814;
						}
						else
						{
							i+=s7r.reg4814;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
			}
			else if((s7r.reg4818&0x40)&&!(s7r.reg4818&0x20))
			{
				s7r.offset_add|=0x01;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						if(s7r.reg4818&0x08)
						{
							short adj;
							adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
							i+=adj;
						}
						else
						{
							uint16 adj;
							adj=(s7r.reg4815<<8)|s7r.reg4814;
							i+=adj;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
				
			}
		}
		
		s7r.written|=0x08;
		break;
		
	//data port adjust high (has a funky immediate increment mode)
	case 0x4815:
		s7r.reg4815=data;
		if(s7r.reg4818&0x02)
		{
			if(s7r.reg4818&0x20&&!(s7r.reg4818&0x40))
			{
				s7r.offset_add|=0x02;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						
						if(s7r.reg4818&0x08)
						{
							i+=(signed char)s7r.reg4814;
						}
						else
						{
							i+=s7r.reg4814;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
			}
			else if(s7r.reg4818&0x40&&!(s7r.reg4818&0x20))
			{
				s7r.offset_add|=0x02;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						if(s7r.reg4818&0x08)
						{
							short adj;
							adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
							i+=adj;
						}
						else
						{
							uint16 adj;
							adj=(s7r.reg4815<<8)|s7r.reg4814;
							i+=adj;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
			}
		}
		s7r.written|=0x10;
		break;
	//data port increment low
	case 0x4816:
		s7r.reg4816=data;
		break;
	//data port increment high
	case 0x4817:
		s7r.reg4817=data;
		break;

	//data port mode switches
	//note that it starts inactive.
	case 0x4818:
		if((s7r.written&0x18)!=0x18)
			break;
		s7r.offset_add=0;
		s7r.reg4818=data;
		break;

	//multiplicand low or dividend lowest
	case 0x4820:
		s7r.reg4820=data;
		break;
	//multiplicand high or dividend lower
	case 0x4821:
		s7r.reg4821=data;
		break;
	//dividend higher
	case 0x4822:
		s7r.reg4822=data;
		break;
	//dividend highest
	case 0x4823:
		s7r.reg4823=data;
		break;
	//multiplier low
	case 0x4824:
		s7r.reg4824=data;
		break;
	//multiplier high (triggers operation)
	case 0x4825:
		s7r.reg4825=data;
		if(s7r.reg482E&0x01)
		{
			int mul;
			short m1=(short)((s7r.reg4824)|(s7r.reg4825<<8));
			short m2=(short)((s7r.reg4820)|(s7r.reg4821<<8));
			
			mul=m1*m2;
			s7r.reg4828=(uint8)(mul&0x000000FF);
			s7r.reg4829=(uint8)((mul&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((mul&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((mul&0xFF000000)>>24);
		}
		else
		{
			uint32 mul;
			uint16 m1=(uint16)((s7r.reg4824)|(s7r.reg4825<<8));
			uint16 m2=(uint16)((s7r.reg4820)|(s7r.reg4821<<8));
			
			mul=m1*m2;
			s7r.reg4828=(uint8)(mul&0x000000FF);
			s7r.reg4829=(uint8)((mul&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((mul&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((mul&0xFF000000)>>24);
		}
		s7r.reg482F=0x80;
		break;
	//divisor low
	case 0x4826:
		s7r.reg4826=data;
		break;
	//divisor high (triggers operation)
	case 0x4827:
		s7r.reg4827=data;
		if(s7r.reg482E&0x01)
		{
			int quotient;
			short remainder;
			int dividend=(int)(s7r.reg4820|(s7r.reg4821<<8)|(s7r.reg4822<<16)|(s7r.reg4823<<24));
			short divisor=(short)(s7r.reg4826|(s7r.reg4827<<8));
			if(divisor != 0)
			{
				quotient=(int)(dividend/divisor);
				remainder=(short)(dividend%divisor);
			}
			else
			{
				quotient=0;
				remainder=dividend&0x0000FFFF;
			}
			s7r.reg4828=(uint8)(quotient&0x000000FF);
			s7r.reg4829=(uint8)((quotient&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((quotient&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((quotient&0xFF000000)>>24);
			s7r.reg482C=(uint8)remainder&0x00FF;
			s7r.reg482D=(uint8)((remainder&0xFF00)>>8);
		}
		else
		{
			uint32 quotient;
			uint16 remainder;
			uint32 dividend=(uint32)(s7r.reg4820|(s7r.reg4821<<8)|(s7r.reg4822<<16)|(s7r.reg4823<<24));
			uint16 divisor=(uint16)(s7r.reg4826|(s7r.reg4827<<8));
			if(divisor != 0)
			{
				quotient=(uint32)(dividend/divisor);
				remainder=(uint16)(dividend%divisor);
			}
			else
			{
				quotient=0;
				remainder=dividend&0x0000FFFF;
			}
			s7r.reg4828=(uint8)(quotient&0x000000FF);
			s7r.reg4829=(uint8)((quotient&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((quotient&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((quotient&0xFF000000)>>24);
			s7r.reg482C=(uint8)remainder&0x00FF;
			s7r.reg482D=(uint8)((remainder&0xFF00)>>8);
		}
		s7r.reg482F=0x80;
		break;
	//result registers are possibly read-only

	//reset: writes here nuke the whole math unit
	//Zero indicates unsigned math, resets with non-zero values turn on signed math
	case 0x482E:
		s7r.reg4820=s7r.reg4821=s7r.reg4822=s7r.reg4823=s7r.reg4824=s7r.reg4825=s7r.reg4826=s7r.reg4827=s7r.reg4828=s7r.reg4829=s7r.reg482A=s7r.reg482B=s7r.reg482C=s7r.reg482D=0;
		s7r.reg482E=data;
		break;

	//math status register possibly read only

	//SRAM toggle
	case 0x4830:
		Memory.SPC7110Sram(data);
		s7r.reg4830=data;
		break;
	//Bank DX mapping
	case 0x4831:
		s7r.reg4831=data;
		break;
	//Bank EX mapping
	case 0x4832:
		s7r.reg4832=data;
		break;
	//Bank FX mapping
	case 0x4833:
		s7r.reg4833=data;
		break;
	//S-RAM mapping? who knows?
	case 0x4834:
		s7r.reg4834=data;
		break;
	//RTC Toggle
	case 0x4840:
		if(0==data)
		{
			S9xUpdateRTC();
			//	rtc_f9.init=false;
			//	rtc_f9.index=-1;
		}
		if(data&0x01)
		{
			s7r.reg4842=0x80;
			//rtc_f9.last_used=time(NULL);//????
			rtc_f9.init=false;
			rtc_f9.index=-1;
		}
		s7r.reg4840=data;
		break;
	//RTC init/command/index register
	case 0x4841:
		if(rtc_f9.init)
		{
			if(-1==rtc_f9.index)
			{
				rtc_f9.index=data&0x0F;
				break;
			}
			if(rtc_f9.control==0x0C)
			{
				rtc_f9.index=data&0x0F;
				s7r.reg4842=0x80;
				rtc_f9.last_used=time(NULL);
			}
			else
			{
				
				if(0x0D==rtc_f9.index)
				{
					if(data&0x08)
					{
						if(rtc_f9.reg[1]<3)
						{
							S9xUpdateRTC();
							rtc_f9.reg[0]=0;
							rtc_f9.reg[1]=0;
							rtc_f9.last_used=time(NULL);
						}
						else
						{
							S9xUpdateRTC();
							rtc_f9.reg[0]=0;
							rtc_f9.reg[1]=0;
							rtc_f9.last_used=time(NULL)-60;
							S9xUpdateRTC();
							rtc_f9.last_used=time(NULL);
						}
						data&=0x07;
					}
					if(rtc_f9.reg[0x0D]&0x01)
					{
						if(!(data%2))
						{
							rtc_f9.reg[rtc_f9.index&0x0F]=data;
							rtc_f9.last_used=time(NULL)-1;
							S9xUpdateRTC();
							rtc_f9.last_used=time(NULL);
						}
					}
				}
				if(0x0F==rtc_f9.index)
				{
					if(data&0x01&&!(rtc_f9.reg[0x0F]&0x01))
					{
						S9xUpdateRTC();
						rtc_f9.reg[0]=0;
						rtc_f9.reg[1]=0;
						rtc_f9.last_used=time(NULL);
					}
					if(data&0x02&&!(rtc_f9.reg[0x0F]&0x02))
					{
						S9xUpdateRTC();
						rtc_f9.last_used=time(NULL);
					}
				}
				rtc_f9.reg[rtc_f9.index&0x0F]=data;
				s7r.reg4842=0x80;
				rtc_f9.index=(rtc_f9.index+1)%0x10;
			}
		}
		else
		{
			if(data==0x03||data==0x0C)
			{
				rtc_f9.init=true;
				rtc_f9.control=data;
				rtc_f9.index=-1;
			}
		}
		break;
	//writes to RTC status register aren't expected to be meaningful
	default:
		Address-=0x4800;
		break;
		//16 BIT MULTIPLIER: ($FF00) high byte, defval:00
	}
}
extern "C"{
//emulate the SPC7110's ability to remap banks Dx, Ex, and Fx.
uint8 S9xGetS7110Byte(uint32 Address)
{
	uint32 i;
	switch((Address&0x00F00000)>>16)
	{
	case 0xD0:
		i=s7r.reg4831*0x00100000;
		break;
	case 0xE0:
		i=s7r.reg4832*0x00100000;
		break;
	case 0xF0:
		i=s7r.reg4833*0x00100000;
		break;
	default:i=0;
	}
	i+=Address&0x000FFFFF;
	i+=s7r.DataRomOffset;
	return ROM[i];
}
}
/**********************************************************************************************/
/* S9xSRTCDaysInMonth()                                                                       */
/* Return the number of days in a specific month for a certain year                           */
/* copied directly for RTC functionality, separated in case of incompatibilities			  */
/**********************************************************************************************/
int	S9xRTCDaysInMonth( int month, int year )
{
    int		mdays;
	
    switch ( month )
    {
	case 2:
		if ( ( year % 4 == 0 ) )    // DKJM2 only uses 199x - 22xx
			mdays = 29;
		else
			mdays = 28;
		break;
		
	case 4:
	case 6:
	case 9:
	case 11:
		mdays = 30;
		break;
		
	default:	// months 1,3,5,7,8,10,12
		mdays = 31;
		break;
    }
	
    return mdays;
}


#define DAYTICKS (60*60*24)
#define HOURTICKS (60*60)
#define MINUTETICKS 60


/**********************************************************************************************/
/* S9xUpdateRTC()									                                          */
/* Advance the RTC time							                                              */
/**********************************************************************************************/

void	S9xUpdateRTC ()
{
	time_t	cur_systime;
	long    time_diff;
	
    // Keep track of game time by computing the number of seconds that pass on the system
    // clock and adding the same number of seconds to the RTC clock structure.
	
    if (rtc_f9.init && 0==(rtc_f9.reg[0x0D]&0x01) && 0==(rtc_f9.reg[0x0F]&0x03))
    {
        cur_systime = time (NULL);
		
        // This method assumes one time_t clock tick is one second
        //        which should work on PCs and GNU systems.
        //        If your tick interval is different adjust the
		//        DAYTICK, HOURTICK, and MINUTETICK defines
		
        time_diff = (long) (cur_systime - rtc_f9.last_used);
		rtc_f9.last_used = cur_systime;
        
        if ( time_diff > 0 )
        {
			int		seconds;
			int		minutes;
			int		hours;
			int		days;
			int		month;
			int		year;
			int		temp_days;
			
			int		year_hundreds;
			int		year_tens;
			int		year_ones;
			
			
			if ( time_diff > DAYTICKS )
			{
				days = time_diff / DAYTICKS;
				time_diff = time_diff - days * DAYTICKS;
			}
			else
			{
				days = 0;
			}
			
			if ( time_diff > HOURTICKS )
			{
				hours = time_diff / HOURTICKS;
				time_diff = time_diff - hours * HOURTICKS;
			}
			else
			{
				hours = 0;
			}
			
			if ( time_diff > MINUTETICKS )
			{
				minutes = time_diff / MINUTETICKS;
				time_diff = time_diff - minutes * MINUTETICKS;
			}
			else
			{
				minutes = 0;
			}
			
			if ( time_diff > 0 )
			{
				seconds = time_diff;
			}
			else
			{
				seconds = 0;
			}
			
			
			seconds += (rtc_f9.reg[1]*10 + rtc_f9.reg[0]);
			if ( seconds >= 60 )
			{
				seconds -= 60;
				minutes += 1;
			}
			
			minutes += (rtc_f9.reg[3]*10 + rtc_f9.reg[2]);
			if ( minutes >= 60 )
			{
				minutes -= 60;
				hours += 1;
			}
			
			hours += (rtc_f9.reg[5]*10 + rtc_f9.reg[4]);
			if ( hours >= 24 )
			{
				hours -= 24;
				days += 1;
			}
			
			year =  rtc_f9.reg[11]*10 + rtc_f9.reg[10];
			year += ( 1900 );
			month = rtc_f9.reg[8]+10*rtc_f9.reg[9];
			rtc_f9.reg[12]+=days;
			days += (rtc_f9.reg[7]*10 + rtc_f9.reg[6]);
			if ( days > 0 )
			{
				while ( days > (temp_days = S9xRTCDaysInMonth( month, year )) )
				{
					days -= temp_days;
					month += 1;
					if ( month > 12 )
					{
						year += 1;
						month = 1;
					}
				}
			}

			year_tens = year % 100;
			year_ones = year_tens % 10;
			year_tens /= 10;
			year_hundreds = (year - 1000) / 100;
			
			rtc_f9.reg[0] = seconds % 10;
			rtc_f9.reg[1] = seconds / 10;
			rtc_f9.reg[2] = minutes % 10;
			rtc_f9.reg[3] = minutes / 10;
			rtc_f9.reg[4] = hours % 10;
			rtc_f9.reg[5] = hours / 10;
			rtc_f9.reg[6] = days % 10;
			rtc_f9.reg[7] = days / 10;
			rtc_f9.reg[8] = month%10;
			rtc_f9.reg[9] = month /10;
			rtc_f9.reg[10] = year_ones;
			rtc_f9.reg[11] = year_tens;
			rtc_f9.reg[12] %= 7;
			return;
        }
    }
}
extern "C"{

//allows DMA from the ROM (is this even possible on the SPC7110?
uint8* Get7110BasePtr(uint32 Address)
{
	uint32 i;
	switch((Address&0x00F00000)>>16)
	{
	case 0xD0:
		i=s7r.reg4831*0x100000;
		break;
	case 0xE0:
		i=s7r.reg4832*0x100000;
		break;
	case 0xF0:
		i=s7r.reg4833*0x100000;
		break;
	default:i=0;
	}
	i+=Address&0x000F0000;
//not that anything DMAs from ROM, but...
//	i+=s7r.DataRomOffset;
	return &ROM[i];
}
//end extern
}

//loads the index into memory.
//index.bin is little-endian
//format index (1)-table(3)-file offset(4)-length(4)
bool Load7110Index(char* filename)
{
	FILE* fp;
	uint8 buffer[12];
	int table=0;
	uint8 index=0;
	uint32 offset=0;
	uint32 size=0;
	int i=0;
	fp=fopen(filename, "rb");
	if(NULL==fp)
		return false;
	do
	{
		i=0;
		fread(buffer, 1, 12,fp);
		table=(buffer[3]<<16)|(buffer[2]<<8)|buffer[1];
		index=buffer[0];
		offset=(buffer[7]<<24)|(buffer[6]<<16)|(buffer[5]<<8)|buffer[4];
		size=(buffer[11]<<24)|(buffer[10]<<16)|(buffer[9]<<8)|buffer[8];
		//while(i<MAX_TABLES&&decompack.tableEnts[i].table!=table)
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table&&decompack->tableEnts[i].table!=0)
			i++;
		if(i==MAX_TABLES)
			return false;
		//added
		decompack->tableEnts[i].table=table;
		//-----
		decompack->tableEnts[i].location[index].offset=offset;
		decompack->tableEnts[i].location[index].size=size;
		decompack->tableEnts[i].location[index].used_len=0;
		decompack->tableEnts[i].location[index].used_offset=0;
		
	}
	while(!feof(fp));
	fclose(fp);
	return true;
}


//Cache 1 load function
void SPC7110Load(char* dirname)
{
	char finder[PATH_MAX];
	char temp_path[PATH_MAX];
	char filename [PATH_MAX + 1];
    char drive [_MAX_DRIVE + 1];
    char dir [_MAX_DIR + 1];
    char fname [_MAX_FNAME + 1];
    char ext [_MAX_EXT + 1];
	int i=0;

	decompack=new Pack7110;

#ifndef _XBOX
	getcwd(temp_path,PATH_MAX);
#endif

	ZeroMemory(decompack, sizeof(Pack7110));
	
    if (strlen (FREEZEFOLDER))
    {
  //      splitpath (Memory.ROMFilename, drive, dir, fname, ext);
        strcpy (filename, FREEZEFOLDER);
#ifndef _XBOX
        strcat (filename, "/");
		chdir(filename);
		if(-1==chdir(dirname))
		{
			S9xMessage(0,0,"Graphics Pack not found!");
			//Settings.SPC7110=false;
			//return;
		}
#else
		strcat (filename,dirname);
		strcat (filename,"\\");
#endif
    }
    else
    {
        splitpath (Memory.ROMFilename, drive, dir, fname, ext);
		strcpy(filename, drive);
		//strcat(filename, "\\");
		strcat(filename, dir);

#ifndef _XBOX
		chdir(filename);
		if(-1==chdir(dirname))
		{
			S9xMessage(0,0,"Graphics Pack not found!");
//			Settings.SPC7110=false;
			//return;
		}
#endif

    }

#ifndef _XBOX
	getcwd(finder, PATH_MAX);
	strcat(finder, "/*.bin");
	Load7110Index("index.bin");
#else
	// D:\\ is always app.path in Xbox
	Load7110Index("d:\\index.bin");
#endif
		//	Load7110Index(index);

	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->tableEnts[i].table!=0)
		{
			char binname[PATH_MAX];
#ifndef _XBOX
			sprintf(binname,"%06X.bin",decompack->tableEnts[i].table);
#else
			sprintf(binname,"%s%06X.bin",filename,decompack->tableEnts[i].table);
#endif
			struct stat buf;
			if(-1!=stat(binname, &buf))
				decompack->binfiles[i]=new uint8[buf.st_size];
			FILE* fp=fopen(binname, "rb");
			if(fp)
			{
				fread(decompack->binfiles[i],buf.st_size,1,fp);
				fclose(fp);
			}
		}
	}

#ifndef _XBOX	
	chdir(temp_path);
#endif

	Copy7110=&MovePackData;
	CleanUp7110=&Del7110Gfx;
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_ENABLED);
	#endif
#endif
}

//Cache 2 load function
void SPC7110Open(char* dirname)
{
	char finder[PATH_MAX];
	char temp_path[PATH_MAX];
	char filename [PATH_MAX + 1];
    char drive [_MAX_DRIVE + 1];
    char dir [_MAX_DIR + 1];
    char fname [_MAX_FNAME + 1];
    char ext [_MAX_EXT + 1];
	int i=0;

	decompack=new Pack7110;

#ifndef _XBOX
	getcwd(temp_path,PATH_MAX);
#endif

	ZeroMemory(decompack, sizeof(Pack7110));
	
    if (strlen (FREEZEFOLDER))
    {
        //splitpath (Memory.ROMFilename, drive, dir, fname, ext);
        strcpy (filename, FREEZEFOLDER);
        
#ifndef _XBOX
		strcat (filename, "/");
		chdir(filename);
		if(-1==chdir(dirname))
		{
						S9xMessage(0,0,"Graphics Pack not found!");
//Settings.SPC7110=false;
//			return;
		}
#else
		strcat (filename,dirname);
		strcat (filename,"\\");

#endif

    }
    else
    {
        splitpath (Memory.ROMFilename, drive, dir, fname, ext);
		strcpy(filename, drive);
		//strcat(filename, "\\");
		strcat(filename, dir);

#ifndef _XBOX
		chdir(filename);
		if(-1==chdir(dirname))
		{
						S9xMessage(0,0,"Graphics Pack not found!");
//			Settings.SPC7110=false;
//			return;
		}
#endif

    }

#ifndef _XBOX
	getcwd(finder, PATH_MAX);
	strcat(finder, "/*.bin");
	Load7110Index("index.bin");
#else
		// D:\\ is always app.path in Xbox
	Load7110Index("d:\\index.bin");
#endif

	for (i=0; i<MAX_TABLES; i++)
		decompack->binfiles[i]=NULL;

	ReadPackData();

#ifndef _XBOX
	chdir(temp_path);
#endif

	Copy7110=&ReadPackData;
	CleanUp7110=&Close7110Gfx;

#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_ENABLED);
	#endif
#endif
}

//Cache 3's load function
void SPC7110Grab(char* dirname)
{
	char finder[PATH_MAX];
	char temp_path[PATH_MAX];
	char filename [PATH_MAX + 1];
    char drive [_MAX_DRIVE + 1];
    char dir [_MAX_DIR + 1];
    char fname [_MAX_FNAME + 1];
    char ext [_MAX_EXT + 1];
	int i=0;

	decompack=new Pack7110;

#ifndef _XBOX
	getcwd(temp_path,PATH_MAX);
#endif

	int32 buffer_size=1024*1024*cacheMegs;//*some setting

	ZeroMemory(decompack, sizeof(Pack7110));
	
    if (strlen (FREEZEFOLDER))
    {
        strcpy (filename, FREEZEFOLDER);
#ifndef _XBOX
        strcat (filename, "/");
		chdir(filename);
		if(-1==chdir(dirname))
		{
			S9xMessage(0,0,"Graphics Pack not found!");
		}
#else
		strcat (filename,dirname);
		strcat (filename,"\\");

#endif
    }
    else
    {
        splitpath (Memory.ROMFilename, drive, dir, fname, ext);
		strcpy(filename, drive);
		strcat(filename, dir);

#ifndef _XBOX
		chdir(filename);
		if(-1==chdir(dirname))
		{
			S9xMessage(0,0,"Graphics Pack not found!");
		}
#endif

    }

#ifndef _XBOX
	getcwd(finder, PATH_MAX);
	strcat(finder, "/*.bin");

	Load7110Index("index.bin");
#else
	// D:\\ is always app.path in Xbox
	Load7110Index("d:\\index.bin");
#endif

	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->tableEnts[i].table!=0)
		{
			char binname[PATH_MAX];
#ifndef _XBOX
			sprintf(binname,"%06X.bin",decompack->tableEnts[i].table);
#else
			sprintf(binname,"%s%06X.bin",filename,decompack->tableEnts[i].table);
#endif
			struct stat buf;
//add load/no load calculations here
			if(-1!=stat(binname, &buf))
			{
				if(buf.st_size<buffer_size)
					decompack->binfiles[i]=new uint8[buf.st_size];
				FILE* fp=fopen(binname, "rb");
				//use them here
				if(fp)
				{
					if(buf.st_size<buffer_size)
					{
						fread(decompack->binfiles[i],buf.st_size,1,fp);
						fclose(fp);
						buffer_size-=buf.st_size;
						decompack->tableEnts[i].is_file=false;
					}
					else
					{
						decompack->binfiles[i]=(uint8*)fp;
						decompack->tableEnts[i].is_file=true;
					}
				}
			}
		}
	}

#ifndef _XBOX
	chdir(temp_path);
#endif

	Copy7110=&GetPackData;
	CleanUp7110=&Drop7110Gfx;


#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_ENABLED);
	#endif
#endif
}

//Cache 1 clean up function
void Del7110Gfx()
{
	int i;
	if(Settings.SPC7110)
	{
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_GRAYED);
	#endif
#endif
		Do7110Logging();
	}
	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->binfiles[i]!=NULL)
		{
			delete []decompack->binfiles[i];
			decompack->binfiles[i]=NULL;
		}
	}
	Settings.SPC7110=false;
	Settings.SPC7110RTC=false;
	if(NULL!=decompack)
		delete decompack;
	decompack=NULL;
	CleanUp7110=NULL;
	Copy7110=NULL;
}

//Cache2 cleanup function
void Close7110Gfx()
{
	int i;
	if(Settings.SPC7110)
	{
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_GRAYED);
	#endif
#endif
		Do7110Logging();
	}
	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->binfiles[i]!=NULL)
		{
			fclose((FILE*)decompack->binfiles[i]);
			decompack->binfiles[i]=NULL;
		}
	}
	Settings.SPC7110=false;
	Settings.SPC7110RTC=false;
	if(NULL!=decompack)
		delete decompack;
	decompack=NULL;
	CleanUp7110=NULL;
	Copy7110=NULL;
}

//cache 3's clean-up code
void Drop7110Gfx()
{
	int i;
	if(Settings.SPC7110)
	{
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_GRAYED);
	#endif
#endif
		Do7110Logging();
	}
	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->binfiles[i]!=NULL)
		{
			if(decompack->tableEnts[i].is_file)
			{
				fclose((FILE*)decompack->binfiles[i]);
				decompack->binfiles[i]=NULL;
			}
			else
			{
				delete []decompack->binfiles[i];
				decompack->binfiles[i]=NULL;
			}
		}
	}
	Settings.SPC7110=false;
	Settings.SPC7110RTC=false;
	if(NULL!=decompack)
		delete decompack;
	decompack=NULL;
	CleanUp7110=NULL;
	Copy7110=NULL;
}

//emulate a reset.
void S9xSpc7110Reset()
{
	s7r.reg4800=0;
	s7r.reg4801=0;
	s7r.reg4802=0;
	s7r.reg4803=0;
	s7r.reg4804=0;
	s7r.reg4805=0;
	s7r.reg4806=0;
	s7r.reg4807=0;
	s7r.reg4808=0;
	s7r.reg4809=0;
	s7r.reg480A=0;
	s7r.reg480B=0;
	s7r.reg480C=0;
	s7r.reg4811=0;
	s7r.reg4812=0;
	s7r.reg4813=0;
	s7r.reg4814=0;
	s7r.reg4815=0;
	s7r.reg4816=0;
	s7r.reg4817=0;
	s7r.reg4818=0;
	s7r.reg4820=0;
	s7r.reg4821=0;
	s7r.reg4822=0;
	s7r.reg4823=0;
	s7r.reg4824=0;
	s7r.reg4825=0;
	s7r.reg4826=0;
	s7r.reg4827=0;
	s7r.reg4828=0;
	s7r.reg4829=0;
	s7r.reg482A=0;
	s7r.reg482B=0;
	s7r.reg482C=0;
	s7r.reg482D=0;
	s7r.reg482E=0;
	s7r.reg482F=0;
	s7r.reg4830=0;
	s7r.reg4831=0;
	s7r.reg4832=1;
	s7r.reg4833=2;
	s7r.reg4834=0;
	s7r.reg4840=0;
	s7r.reg4841=0;
	s7r.reg4842=0;
	s7r.written=0;
	s7r.offset_add=0;
	s7r.AlignBy=1;
	s7r.bank50Internal=0;
	memset(s7r.bank50,0x00,DECOMP_BUFFER_SIZE);
}


//outputs a cumulative log for the game.
//there's nothing really weird here, just
//reading the old log, and writing a new one.
//note the logs are explicitly little-endian, not host byte order.
void Do7110Logging()
{
	uint8 ent_temp;
	FILE* flog;
	int entries=0;
	
	if(Settings.SPC7110)
	{
		//flush last read into logging
		(*Copy7110)();
		
		if(!strncmp((char*)&Memory.ROM [0xffc0], "SUPER POWER LEAG 4   ", 21))
		{
#ifdef _XBOX
			flog=fopen("T:\\spl4-sp7.dat","rb");
#else
			flog=fopen("spl4-sp7.dat","rb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "MOMOTETSU HAPPY      ",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\smht-sp7.dat","rb");
#else
			flog=fopen("smht-sp7.dat","rb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "HU TENGAI MAKYO ZERO ", 21))
		{
#ifdef _XBOX
			flog=fopen("T:\\feoezsp7.dat","rb");
#else
			flog=fopen("feoezsp7.dat","rb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "JUMP TENGAIMAKYO ZERO",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\sjumpsp7.dat","rb");
#else
			flog=fopen("sjumpsp7.dat","rb");
#endif
		}
		else
		{
#ifdef _XBOX
			flog=fopen("T:\\misc-sp7.dat","rb");
#else
			flog=fopen("misc-sp7.dat","rb");
#endif
		}
		
		if(flog)
		{
			uint8 buffer[8];
			int table=0;
			uint16 offset=0;
			uint16 length=0;
			fseek(flog, 35,0);
			do
			{
				int i=0;
				Data7110 *log=NULL;
				fread(buffer, 1, 8, flog);
				table=buffer[0]|(buffer[1]<<8)|(buffer[2]<<16);
				offset=buffer[6]|(buffer[7]<<8);
				length=buffer[4]|(buffer[5]<<8);
				while(i<MAX_TABLES&&log==NULL)
				{
					if(decompack->tableEnts[i].table==table)
					{
						log=&(decompack->tableEnts[i].location[(buffer[3])]);
						if((log->used_offset+log->used_len)<(offset+length))
						{
							log->used_offset=offset;
							log->used_len=length;
						}
					}
					i++;
				}
			}
			while(!feof(flog));
			fclose(flog);
		}
		
		
		if(!strncmp((char*)&Memory.ROM [0xffc0], "SUPER POWER LEAG 4   ", 21))
		{
#ifdef _XBOX	// cwd could be the dvd-rom, so write to T:\\ which is storage region for each title
			flog=fopen("T:\\spl4-sp7.dat","wb");
#else
			flog=fopen("spl4-sp7.dat","wb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "MOMOTETSU HAPPY      ",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\smht-sp7.dat","wb");
#else
			flog=fopen("smht-sp7.dat","wb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "HU TENGAI MAKYO ZERO ", 21))
		{
#ifdef _XBOX
			flog=fopen("T:\\feoezsp7.dat","wb");
#else
			flog=fopen("feoezsp7.dat","wb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "JUMP TENGAIMAKYO ZERO",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\sjumpsp7.dat","wb");
#else
			flog=fopen("sjumpsp7.dat","wb");
#endif
		}
		else
		{
#ifdef _XBOX
			flog=fopen("T:\\misc-sp7.dat","wb");
#else
			flog=fopen("misc-sp7.dat","wb");
#endif
		}
		//count entries
		if(flog)
		{
			int j=0;
			int temp=0;
			for(j=0;j<MAX_TABLES;j++)
			{
				for(int k=0;k<256;k++)
				{
					if(decompack->tableEnts[j].location[k].used_len!=0)
						entries++;
				}
			}
			ent_temp=entries&0xFF;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=(entries>>8)&0xFF;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=(entries>>16)&0xFF;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=(entries>>24)&0xFF;
			fwrite(&ent_temp,1,1,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			
			ent_temp=0;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=0;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=0;
			fwrite(&ent_temp,1,1,flog);
			
			for(j=0;j<MAX_TABLES;j++)
			{
				for(int k=0;k<256;k++)
				{
					if(decompack->tableEnts[j].location[k].used_len!=0)
					{
						ent_temp=decompack->tableEnts[j].table&0xFF;
						fwrite(&ent_temp,1,1,flog);//801
						ent_temp=(decompack->tableEnts[j].table>>8)&0xFF;;
						fwrite(&ent_temp,1,1,flog);//802
						ent_temp=(decompack->tableEnts[j].table>>16)&0xFF;;
						fwrite(&ent_temp,1,1,flog);//803
						ent_temp=k&0xFF;
						fwrite(&ent_temp,1,1,flog);//804
						ent_temp=decompack->tableEnts[j].location[k].used_len&0xFF;
						fwrite(&ent_temp,1,1,flog);//lsb of 
						ent_temp=(decompack->tableEnts[j].location[k].used_len>>8)&0xFF;
						fwrite(&ent_temp,1,1,flog);//msb of
						ent_temp=(decompack->tableEnts[j].location[k].used_offset)&0xFF;
						fwrite(&ent_temp,1,1,flog);//lsb of
						ent_temp=(decompack->tableEnts[j].location[k].used_offset>>8)&0xFF;
						fwrite(&ent_temp,1,1,flog);//msb of
					}
				}
			}
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fclose(flog);
		}
	}
}
